package com.mi.minal.common.config;

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.mi.minal.common.Interceptor.Auth;
import com.mi.minal.module.system.service.SystemService;
import com.mi.rule.util.EnvKit;
import com.mi.rule.util.project.MiException;
import com.mi.rule.util.project.SRS;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * 项目配置
 *
 * @author 王永吉
 * <p>
 * 1.拦截器配置
 * 2.资源配置
 * 3.跨域配置
 * 4.json配置
 * 51.异常处理
 * 52.异常处理
 * <p>
 * 98.初始化完成
 * 99.启动完成
 */
@Slf4j
@Configuration
@RestControllerAdvice
public class WebConfig implements WebMvcConfigurer, ApplicationListener<WebServerInitializedEvent>, ApplicationContextAware {
    //全局拦截器
    @Resource
    private Auth authInterceptor;

    /**
     * 1.拦截器配置
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry
                .addInterceptor(authInterceptor)
                .addPathPatterns("/**");
        log.info("========================= 拦截器注册完成 =========================");
    }

    /**
     * 2.资源配置
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("========================= 资源配置完成 =========================");
    }

    /**
     * 3.跨域配置
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                .allowedOrigins("*")
                // 是否允许证书 不再默认开启
                .allowCredentials(true)
                // 设置允许的方法
                .allowedMethods("*")
                //跨域允许时间
                .maxAge(3600);
        log.info("========================= 跨域配置完成 =========================");
    }

    /**
     * 4.json
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return jacksonObjectMapperBuilder -> {
            jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
            jacksonObjectMapperBuilder.serializerByType(Long.TYPE, ToStringSerializer.instance);
            jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            jacksonObjectMapperBuilder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            jacksonObjectMapperBuilder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            jacksonObjectMapperBuilder.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
            jacksonObjectMapperBuilder.deserializerByType(LocalTime.class, new LocalTimeDeserializer((DateTimeFormatter.ofPattern("HH:mm:ss"))));
        };

    }

    /**
     * 51.未知异常处理
     */
    @ExceptionHandler(value = Exception.class)
    public SRS exceptionHandler(Throwable ex) {
        if (EnvKit.showRequestLog()) {
            return SRS.byError();
        }
        log.error(ExceptionUtils.getStackTrace(ex));
        return SRS.byError();
    }

    /**
     * 52.自定义异常处理
     */
    @ExceptionHandler(value = MiException.class)
    public SRS miExceptionHandler(MiException ex) {
        if (EnvKit.showRequestLog()) {
            return ex.getResponse();
        }
        log.error(ExceptionUtils.getStackTrace(ex));
        return ex.getResponse();
    }

    @Resource
    private SystemService systemService;

    //98.初始化完成
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //log.info("========================= 99999999999999999999999999999999 ===========================");
        //系统资源清理和添加
        systemService.systemResourceCleanAdd(null);
        //角色资源菜单缓存
        systemService.refreshRoleMenuAndResource(null);
        //修改版本号
        systemService.updateVersion(null);
    }

    //99.Spring启动完成
    @Override
    public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {
        //        log.info("========================= onApplicationEvent ===========================");
    }


}
///**
// * 启动流程
// * main方法启动
// * 执行 读取配置文件 此时可以实现EnvironmentAware接口 获取配置文件对象
// * 然后 创建容器对象 此时可以实现ApplicationContextAware 接口 获取容器实例
// * 然后 实例化自定义的Bean对象
// * 然后 配置拦截器
// * 然后 配置跨域
// * 容器创建完成 此时可以实现ApplicationListener接口继续执行其他逻辑
// */